package org.zjvis.datascience.service;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import com.aliyuncs.exceptions.ClientException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zjvis.datascience.common.constant.UserConstant;
import org.zjvis.datascience.common.dto.sms.SmsDTO;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.util.AliyunSmsUtil;
import org.zjvis.datascience.common.util.RedisUtil;

/**
 * @description SMS 验证短信服务 Service
 * @date 2021-12-13
 */
@Service
public class SmsService {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private AliyunSmsUtil aliyunSmsUtil;

    /**
     * @Function: 短信验证
     * @param:    phone 手机号, type 修改类型
     */
    public boolean smsSendMsg(String phone, String type) {
        try {
            setRedisByPhone(phone, type);
            aliyunSmsUtil.sendSms(phone, type);
            return true;
        } catch (ClientException e) {
            throw new DataScienceException(BaseErrorCode.USER_SEND_MESSAGE_ERROR);
        }
    }

    /**
     * @Function: 发送短信时设置Redis缓存
     * @param:    phone 手机号, type 修改类型
     */
    private void setRedisByPhone(String phone, String type) {
        String key = getKey(phone, type);
        setRedisByKey(key);
    }

    /**
     * @Function: 根据key设置redis
     * @param:    key
     */
    private void setRedisByKey(String key) {
        long time = getTime();
        SmsDTO value;
        if(redisUtil.get(key) == null) { // 第一次发送短信
            value = new SmsDTO();
            value.setAlreadySendMsg(1);
            value.setLastMsgTime(time);
            value.setVerified(false);
            redisUtil.set(key, value, UserConstant.RECORD_EFFECTIVE_TIME);
        } else { // 第n次发送短信
            value = (SmsDTO) redisUtil.get(key);
            int times = value.getAlreadySendMsg();
            value.setAlreadySendMsg(times+1);
            value.setLastMsgTime(time);
            value.setVerified(false);
            long expire = redisUtil.getExpire(key);
            redisUtil.set(key, value, expire);
        }
    }

    /**
     * @Function: 获取当前时间
     */
    private long getTime() {
        return System.currentTimeMillis()/1000;
    }

    /**
     * @Function: 根据phone和type获取value对象
     * @param:    phone 手机号, type 修改类型
     */
    public SmsDTO getValue(String phone, String type) {
        String key = getKey(phone, type);
        SmsDTO value = (SmsDTO) redisUtil.get(key);
        return value;
    }

    public String getKey(String phone, String type) {
        String key = "";
        switch (type) {
            case UserConstant.TYPE_OF_REGISTER:
                key = UserConstant.REGISTER_PREFIX + phone;
                break;
            case UserConstant.TYPE_OF_MODIFY:
                key = UserConstant.MODIFY_PREFIX + phone;
                break;
            case UserConstant.TYPE_OF_BIND:
                key = UserConstant.BIND_PREFIX + phone;
                break;
            case UserConstant.TYPE_OF_LOGIN:
                key = UserConstant.LOGIN_PREFIX + phone;
                break;
        }
        return key;
    }

    /**
     * 创建图片验证码
     * @param phone 手机号
     * @return 图片验证码
     */
    public ShearCaptcha createCaptcha(String phone) {
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(368, 112, 4, 4);
        String key = UserConstant.CAPTCHA_PREFIX + phone;
        redisUtil.set(key, captcha.getCode(), UserConstant.EFFECTIVE_TIME);
        return captcha;
    }

    /**
     * 校验图形验证码是否正确
     * @param phone 手机号 captcha 验证码
     */
    public boolean checkCaptcha(String phone, String captcha) {
        String key = UserConstant.CAPTCHA_PREFIX + phone;
        if(redisUtil.get(key) != null) {
            String value = (String) redisUtil.get(key);
            redisUtil.del(key);
            if(value.equals(captcha)) {
                return true;
            }
        }
        throw new DataScienceException(BaseErrorCode.USER_CAPTCHA_CODE_ERROR);
    }

    /**
     * 检查是否可发送手机验证码
     * @param phone 手机号, type 类型
     */
    public void msgAuth(String phone, String type) {
        long time = getTime();
        SmsDTO value = getValue(phone, type);
        if(value == null) { // 还没发过
            return;
        }
        if(((time - value.getLastMsgTime()) < UserConstant.PROHIBIT_TIME) || (value.getAlreadySendMsg() >= UserConstant.USER_PHONE_LIMIT_COUNT)){ // 请求频繁
            throw new DataScienceException(BaseErrorCode.USER_PHONE_CODE_CANNOT_EXCEED_TIMES);
        } else { // 需要图片验证码
            throw new DataScienceException(BaseErrorCode.USER_NEED_CAPTCHA_CODE);
        }
    }

    /**
     * 校验短信验证码是否正确
     * @param phone 手机号 code 验证码 type 请求类型
     */
    public boolean verifyMsgCode(String phone, String code, String type) {
        long time = getTime();
        SmsDTO value = getValue(phone, type);
        if(value != null && ((time - value.getLastMsgTime()) <= UserConstant.EFFECTIVE_TIME) && (value.getVerifyCode().equals(code))) {
            value.setVerified(true);
            String key = "";
            if(type.equals(UserConstant.TYPE_OF_MODIFY) || type.equals(UserConstant.TYPE_OF_BIND)) {
                if(type.equals(UserConstant.TYPE_OF_MODIFY)) {
                    key = UserConstant.MODIFY_PREFIX + phone;
                }else {
                    key = UserConstant.BIND_PREFIX + phone;
                }
                long expire = redisUtil.getExpire(key);
                redisUtil.set(key, value, expire);
            }
            return true;
        } else if (value != null && ((time - value.getLastMsgTime()) > UserConstant.EFFECTIVE_TIME)) {
            throw new DataScienceException(BaseErrorCode.USER_PHONE_CODE_EXPIRED);
        } else {
            throw new DataScienceException(BaseErrorCode.USER_PHONE_CODE_ERROR);
        }
    }
}
